package 网络编程.课后作业;

import 网络编程.课堂案例.M2.ChatClient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 7）、基于TCP协议实现聊天室群聊功能，必须支持一个服务器支持多个客户端
 * ，可以采用多线程实现，为每一个客户端专门开启一个工作线程服务，
 *  并考虑这种方式好不好，如果不好有哪些优化手段。
 */
public class M7 {
    //服务器IP
    private static final String server_ip = "47.105.174.110";
    //服务器端口
    private final static int port = 8080;

    //创建一个线程池
    static ExecutorService executorService = Executors.newSingleThreadExecutor();

    //创建键盘扫描器
    static Scanner input = new Scanner(System.in);

    public static void main(String[] args) {
        Socket socket = null;

        BufferedReader reader = null;
        try{
            socket = new Socket(server_ip,port);

            while (true){
                //向线程池提交任务
                executorService.execute(new M7.SendMessageThread(socket));

                reader  = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String line = reader.readLine();
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class SendMessageThread implements Runnable{
        private Socket socket;
        PrintWriter writer = null;

        public SendMessageThread(Socket socket){
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                String message = input.nextLine();
                writer = new PrintWriter(this.socket.getOutputStream(), true);
                writer.println(message);
                writer.flush();
            }
            catch (Exception ex){
                ex.printStackTrace();
            }
        }
    }
}
class ChatServer{
    //存放所有的客户端套接字
    private static List<Socket> socketList = new ArrayList<>();

    /**
     * 服务器启动方法
     * @param port  服务器监听的端口
     */
    public static void startupServer(int port) throws IOException {
        ServerSocket server = null;
        server = new ServerSocket(port);
        System.out.println("聊天室已启动...");
        while(true){//服务器不能停，一直监听客户端的连接请求
            final Socket client = server.accept();//阻塞方法，接收客户端连接请求
            socketList.add(client);
            //获取远程客户端相关信息   IP地址和端口信息
            int remotePort = client.getPort();
            String hostAddress = client.getInetAddress().getHostAddress();
            System.out.println(hostAddress + ":" + remotePort + "客户端进入聊天室");
            System.out.println("当前聊天的在线总人数为：" + socketList.size());
            new Thread(new ChatServer.ServerThreadHandler(socketList,client)).start();
        }
    }

    //内部类实现多线程  每当一个客户端成功与服务器建立连接，开启一个新的线程服务
    private static class ServerThreadHandler implements Runnable{

        private List<Socket> socketList;
        private Socket socket = null;

        public ServerThreadHandler(List<Socket> socketList,Socket socket){
            this.socketList = socketList;
            this.socket = socket;
        }

        @Override
        public void run() {
            System.out.println("当前线程对象名称为：" + Thread.currentThread().getName() + "为" + this.socket.getInetAddress().getHostAddress()
                    + ":" + this.socket.getPort() + "服务...");
            BufferedReader in = null;
            try{
                while(true) {
                    in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), "UTF-8"));
                    //服务器端读取客户端发送过来的数据
                    String msg = in.readLine();
                    //只要有一个客户端发送消息，立刻马上把信息分发给每一个客户端，从而达到群聊的功能
                    dispatch(msg, socket, this.socketList);
                }
            }catch (Exception e){
                String remoteIP = this.socket.getInetAddress().getHostAddress();
                int port = this.socket.getPort();
                System.out.println(remoteIP + ":" + port + "已离开聊天室");
                this.socketList.remove(this.socket);
                if(this.socket != null && !this.socket.isClosed()){
                    try {
                        this.socket.close();
                        this.socket = null;
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
                System.out.println("当前聊天室在线总人数为：【" + this.socketList.size() + "】人");
            }
        }

        //给每一个客户端分发信息
        private void dispatch(String msg, Socket socket,List<Socket> socketList) {
            PrintWriter out = null;
            if(socketList == null || socketList.size() == 0) {
                return;
            }
            if(socketList != null && socketList.size() > 0){
                for (Socket client : socketList) {
                    if(client.equals(socket)) {// 自己发的信息不要再发给自己了，排除自己
                        continue;
                    }
                    try {
                        out = new PrintWriter(client.getOutputStream(),true);
                        out.println(msg);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }finally {
                        if(out != null){
                            out.close();
                        }
                        if(socket != null){
                            try {
                                socket.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    }

    //程序入口
    public static void main(String[] args) {
        try {
            startupServer(8888);
        } catch (IOException e) {
            System.out.println("无法连接服务器，请检查网络配置，错误信息为:" + e.getMessage());
        }
    }
}
